package com.laopan.wxpay.paycallback;

import com.laopan.wxpay.bean.callback.CallbackHeader;
import com.laopan.wxpay.bean.callback.CallbackResult;
import com.laopan.wxpay.bean.callback.PayCallBackResult;
import com.laopan.wxpay.bean.callback.Resource;
import com.laopan.wxpay.bean.certificate.MerchantPrivateKey;
import com.laopan.wxpay.config.WxPayConfig;
import com.laopan.wxpay.request.certificates.DefaultPrivateKeyManager;
import com.laopan.wxpay.request.certificates.MemoryCacheCertificatesManager;
import com.laopan.wxpay.request.certificates.PrivateKeyManager;
import com.laopan.wxpay.request.certificates.WxCertificatesManager;
import com.laopan.wxpay.utils.JsonUtils;
import com.laopan.wxpay.utils.SignUtils;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

/**
 * 微信回调处理封装
 * @author laopan
 * @className PayCallBack
 * @date 2021/4/29 11:06
 */

public class PayCallBack {
    private WxPayConfig wxPayConfig;

    public PayCallBack(WxPayConfig wxPayConfig) {
        this.wxPayConfig = wxPayConfig;
    }

    /**
     * 真实的回调处理流程
     * @param body 回调的数据
     * @param callbackHeader 微信v3版本回调的请求头信息
     * @param payCallbackExecProcessor 回调执行器
     * @return 回调处理结果
     */
    public CallbackResult callback(String body, CallbackHeader callbackHeader,PayCallbackExecProcessor payCallbackExecProcessor){
        if(payCallbackExecProcessor==null){
            return new CallbackResult("FAIL","不存在回调执行处理");
        }
        if (!checkData(body ,callbackHeader) ) {
            return new CallbackResult("FAIL","验签失败");
        }
        PayCallBackResult payCallBackData= JsonUtils.fromJson(body,PayCallBackResult.class);
        String realData;
        try {
             realData= getRealCallbackData(payCallBackData.getResource());
        } catch (Exception e) {
            return new CallbackResult("FAIL","解密数据失败");
        }
        if(realData==null||realData.length()==0){
            return new CallbackResult("FAIL","未获取到解密数据");
        }
        if(payCallbackExecProcessor.processor(payCallBackData.getEventType(),realData)){
            return new CallbackResult("SUCCESS","成功");
        }
        return new CallbackResult("FAIL","业务处理失败");
    }

    /**
     * 获取真实的回调数据
     * @param resource  回调原数据
     * @return  解密后的数据
     * @throws GeneralSecurityException GeneralSecurityException异常
     * @throws IOException IOException异常
     */
    public  String getRealCallbackData(Resource resource) throws GeneralSecurityException, IOException {
        byte[] apiV3key =wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8);
        AesUtil aes = new AesUtil(apiV3key);
        return aes.decryptToString(
                resource.getAssociatedData()
                        .getBytes(StandardCharsets.UTF_8),
                resource.getNonce()
                        .getBytes(StandardCharsets.UTF_8),
                resource.getCipherText());
    }

    /**
     * 验证数据正确性
     * @param body 返回数据
     * @param callBackHeader 返回头信息
     * @return 是否正确
     */
    public  boolean checkData(String body, CallbackHeader callBackHeader) {
        String mchId = wxPayConfig.getMchId();
        PrivateKeyManager privateKeyManager = new DefaultPrivateKeyManager(wxPayConfig);
        final MerchantPrivateKey merchantPrivateKey = privateKeyManager.getPrivateKey(mchId);
        PrivateKey privateKey = merchantPrivateKey.getPrivateKey();
        WxCertificatesManager certificatesManager = new MemoryCacheCertificatesManager(merchantPrivateKey);
        X509Certificate certificate;
        String useCertSerialNo=certificatesManager.getUseSerialNo(mchId);
        if(useCertSerialNo==null||useCertSerialNo.trim().length()==0 ||useCertSerialNo.equals(callBackHeader.getSerialNo())){
            certificate= certificatesManager.refresh(mchId);
        }else{
            certificate = certificatesManager.getValidCertificate(mchId);
        }
        return SignUtils.verify(certificate,callBackHeader.packageSign(body),callBackHeader.getSign());
    }
}
